學會運用瀏覽器外掛的【頁面內容】【彈出頁面】【背景服務】,
瀏覽器外掛對你來說,應該已經不算難事,
終於可以把它做為你口袋裡的一個好用工具了。
目前我們的翻譯工具,也算是來到相當好用的程度,
git clone https://github.com/betterTrans/betterTranslation.git
但我們當然不會只滿足於此囉。
單純的翻譯修改,其實還是有很多重複的工作。
比方說,如果遇到已經翻譯過的句子,
出現在文章的其他位置,
或許就應該自動套入之前的翻譯,
而不需要再重新翻譯一次。
就算不是自動套入,也應該以某種方式出現在提示中,
讓我們很方便加以引用。
再舉個例子,
當我們看到一些像是「內存」這樣的大陸用語,
想換成更道地的「記憶體」,
修改起來雖然很簡單,
但如果同樣的用詞在同一頁出現好幾十次甚至上百次,
我們就要做幾十次、上百次的重複工作。
這種情況一般最常用的就是「搜尋/取代」功能,
這功能雖然好用,但也可能製造出新的問題。
舉例來說,
我們若把「內存」全部無腦替換成「記憶體」,
就會遇到「海內存知己」變成「海記憶體知己」
這類奇怪的結果。
如果在替換的時候,程式可以自動參考原文、
(比如前一句原文肯定沒有 memory 所以不用修改)
或是根據詞語在句中不同的詞性與角色,
判斷是否應該進行替換,
甚至根據這些額外的「線索」,做出更聰明的修改,
那不就太好了嗎?
這就是所謂的翻譯輔助工具應該做到的其中一種功能。
而目前我們這個很單純的翻譯界面,還看不到這樣的功能。
我們需要一個好用的界面,來引入這些好用的功能。
今天我們就先來製作一個簡單的面板功能,
之後再用它來製作【句子面板】、【詞語面板】之類的界面,
除了可用來呈現一些有用的資訊,
也可以協助我們更聰明的編輯翻譯內容。
之前我們所用的翻譯編輯界面,
主要就是把各個包在 sent 標籤裡的句子,
變成一個 textarea 文字輸入框,
這樣的做法雖然直觀,但有以下幾個缺點:
只要另外建立一個【句子面板】,就能解決這些問題。
其實除了【句子面板】之外,我還想建立一個【詞語面板】。
句子面板用來放句子,所以需要比較寬一點的寬度,
我想把它放在頁面的下方。
詞語面板可用來顯示詞語相關資訊,
我打算放在頁面的右邊。
我希望這兩個面板,都可以用滑動的方式,
從下面和右邊滑動進來,
不需要的時候,也可以滑動出去隱藏起來。
若想做到滑動面板的功能,
其實可以嘗試尋找現成的 JS 專案,
但由於我想客製出自己所需的面板,
相應的程式碼也不困難,
所以,今天就來嘗試看看怎麼做吧。
首先,我們在 js 目錄下建立一個 panels.js,
並在 css 目錄下建立一個 panels.css,
然後記得在 manifest.json 裡宣告一下。
"content_scripts": [ {
...
"css": [
...
"css/panels.css"
],
"js": [
...
"js/panels.js"
]
} ],
這兩個檔案,
主要就是用來整合面板相關的所有程式碼。
一開始我們先在 panels.js 裡,
建立一個叫做 createPanel() 的函式:
function createPanel(id, position = 'bottom', display=true, size = '300px', background_color = '', z_index = 0, opacity = 0) {
...
}
這個函式會先建立一個 id 為 bt_panels 的 div 區塊,
掛進原始網頁的末尾處,以做為放置其他面板的主要區塊。
後續我們所有的面板,全都會放在這個區塊中。
panels = document.createElement("div");
panels.id = "bt_panels"
document.body.append(panels)
接著再根據函式所設定的參數,建立相應的面板。
除了最重要的 id 參數之外,
我們還會在 class 屬性中,設定 bt_panel 做為其類別。
panel = document.createElement("div");
panel.id = id;
panel.classList.add('bt_panel')
在這樣的做法下,
我們就可以在 panels.css 檔案中,
針對 bt_panel 這個類別,定義一些面板通用的 css 設定。
div.bt_panel {
position: fixed; // 脫離原內容位置,以便直接定義區塊位置
...
}
這個函式也會根據 position 位置參數,
決定面板所要擺放的位置。
做法上也是設定 class 屬性:
panel.classList.add(position)
然後在 css 檔案中定義相應的 css 設定。
/* 上面板 */
div.bt_panel.top { ... }
/* 下面板 */
div.bt_panel.bottom {
bottom: 0; // 靠到畫面下緣
left: 0; // 靠到畫面左緣
width: 100%; // 延展至全寬
}
/* 左面板 */
div.bt_panel.left { ... }
/* 右面板 */
div.bt_panel.right { ... }
面板的大小,則是直接根據 size 參數,
參考面板的不同位置進行設定:
if (position === 'left' || position === 'right') {
panel.style.width = size;
}
else if (position === 'top' || position === 'bottom') {
panel.style.height = size;
}
其他設定陸續設好之後,
就可以把面板掛進一開始建立的那個主面板裡頭了。
panels.appendChild(panel);
有了這個函式,
我們就可以透過 window.load 的方式,
直接調用這個函式,
這樣就可以看到所建立的面板了:
window.addEventListener('load', (e)=>{
// 建立一個【句子面板】和一個【單詞面板】
sent_panel = createPanel("bt_sent_panel", "bottom")
token_panel = createPanel("bt_token_panel", "right")
}
由於我們主要是利用 class 類別,搭配相應的 css 設定,
來定義面板的呈現方式,
因此這裡稍微簡單說明一下相關 css 設定所產生的一些效果:
接著我們來實現面板的滑動功能。
我們會建立 slideInPanel() 和 slideOutPanel() 兩個函式,
負責面板的滑入與滑出。
面板滑入滑出的功能,
主要是靠 transform 和 transition-duration 這兩個 css 設定。
transform 可搭配 translate(x, y) 的設定值,
負責「轉移」面板的位置;
transition-duration 則是設定轉移所花費的時間。
舉例來說,
如果想用 0.3 秒的時間,把面板往左滑動 100 px,
可以採用下面的做法:
panel.style['transition-duration'] = '300ms';
// panel.style.transitionDuration = '300ms'; // 這種設定方式也可以
panel.style.transform = 'translate('-100px', 0)'
以 slideOutPanel() 這個把面板滑出去的函式為例,
我們首先會用 on/off 這兩個不同的 class 值,
來設定面板「滑進來/滑出去」的不同狀態,
然後再根據面板所在的 position 位置與大小,
決定要往哪個方向滑動,滑動多少距離(movement)。
if (panel.classList.contains('top')) {
movement = `0px, -${panel.offsetHeight+20}px`;
} else if (panel.classList.contains('bottom')) {
movement = `0px, ${panel.offsetHeight+20}px`;
} else if (panel.classList.contains('right')) {
movement = `${panel.offsetWidth+20}px, 0px`;
} else if (panel.classList.contains('left')) {
movement = `-${panel.offsetWidth+20}px, 0px`;
}
panel.style.transform = 'translate(' + movement + ')'
至於 slideInPanel() 要把面板滑進來,
就不用那麼麻煩,只要滑回原來的位置即可:
panel.style.transform = 'translate(0px, 0px)'
顯示面板時,網頁原本的內容會被遮擋住,
如果不希望面板遮擋住內容,
可以針對原本網頁的 body 標籤,設定相應的 margin 值。
if (!overlay) {
console.log(position)
if (panel.classList.contains('top')) {
document.body.style['margin-top'] = (panel.offsetHeight + 10) + 'px';
} else if (panel.classList.contains('right')) {
document.body.style['margin-right'] = (panel.offsetWidth + 10) + 'px';
} else if (panel.classList.contains('bottom')) {
document.querySelector("#bt_panels").style.height = (panel.offsetHeight + 10) + 'px';
} else if (panel.classList.contains('left')) {
document.body.style['margin-left'] = (panel.offsetWidth + 10) + 'px';
}
}
這裡特別說明一下的是,
設定 margin-bottom 不一定有用,
但既然我們的 #bt_panels 主面板一定是 body 最後一個 div,
就用它的 height 來做設定,
顯示正確的機率會更高一點。
最後我們可以再寫一個 togglePanel(),
負責面板的開關切換。
if (panel.classList.contains('on')) {
slideOutPanel(id, position);
} else {
slideInPanel(id, position);
}
至此,面板功能就相當完善了。
我們可以設定一個測試用的快速鍵(Alt+9),
試著切換剛才所建立的面板:
hotkey_handlers = {
...
'Alt9': Alt9,
...
}
...
function Alt9(){
togglePanel('bt_sent_panel')
togglePanel('bt_token_panel')
}
還記得嗎?如果想讓使用者自定義快速鍵,
manifest.json 的 commands 裡還要再添加一個項目:
"Alt9": {
"description": "測試新功能"
},
好啦!今天的任務總算完成。
有了這個面板功能,
隨後我們就可以在面板放入其他想要的功能了。
接下來我們打算運用 Vue3,
來實現面板中的各項功能。
明天請拭目以待囉。